#ifndef ARRAY_STORAGE_H
#define ARRAY_STORAGE_H

/**
 *  @brief  a class for general array storage
 *  @tparam DIM array dimension
 *
 *  General array storage can be determined by 1) storage order and 
 *  2) base index. For example, c-style array storage order is from
 *  higher to lower, i.e., (2, 1, 0) for 3-dim. array, and base index
 *  is (0, 0, 0). On the other hand, fortran-style array storage is
 *  from lower to higher, i.e., (0, 1, 2) for 3-dim. array, and base
 *  index is (1, 1, 1)
 *
 *  The two specialized cases are designed as two public static intefaces
 *  c_array() and fortran_array(). They generate c-style and fortran-style
 *  array storage respectively. 
 */
template <unsigned int DIM>
class array_storage
{
public:
	array_storage() {}
	~array_storage() {}
	
	array_storage( const array_storage<DIM>& arr_store ) 
	{ *this = arr_store; }
	
	array_storage( array_storage<DIM>&& arr_store ) 
	{ 
		order_ = std::move(arr_store.order_); 
		base_ = std::move(arr_store.base_);
	}

	array_storage( const tiny_vector<unsigned int, DIM>& order, const tiny_vector<int, DIM>& base ) 
	: order_(order), base_(base) {}

	array_storage& operator=( const array_storage<DIM>& arr_store ) 
	{ 
		order_ = arr_store.order_; 
		base_ = arr_store.base_;
		return *this; 
	}
	
	const tiny_vector<unsigned int, DIM>& get_storage_order() const { return order_; }
	void set_storage_order( const tiny_vector<unsigned int, DIM>& order ){ order_ = order; }
	
	unsigned int	get_storage_order( unsigned int dim ) const 		 { return order_[dim]; }
	void		set_storage_order( unsigned int dim, unsigned int order ){ order_[dim] = order; }
	
	const tiny_vector<int, DIM>&	get_base() const { return base_; }
	void set_base( const tiny_vector<int, DIM>& base ) { base_ = base; }
	
	int 	get_base( unsigned int dim ) const 	{ return base_[dim]; }
	void 	set_base( unsigned int dim, int base )	{ base_[dim] = base; }

	//! compute strides in according to storage order
	tiny_vector<int, DIM> compute_storage_stride( const tiny_vector<int, DIM>& shape ) const
	{
		tiny_vector<int, DIM> stride;
		stride[ order_[0] ] = 1;
		int off = shape[ order_[0] ];
		for( int dim = 1; dim < DIM; dim++)
		{
			stride[ order_[dim] ] = off;
			off *= shape[ order_[dim] ];
		}
		return stride;
	}
	
	//! static public interface to create a c-style array storage
	inline static array_storage<DIM> c_array() 
	{
		tiny_vector<unsigned int, DIM> order;
		for( int dim = 0; dim < DIM; dim++ )
			order[dim] = DIM - dim - 1;
		return array_storage<DIM>(order, 0); 
	}
	
	//! static pubic interface to create fortran-style array storage
	inline static array_storage<DIM> fortran_array() 
	{
		tiny_vector<unsigned int, DIM> order;
		for( int dim = 0; dim < DIM; dim++ )
			order[dim] = dim;
		return array_storage<DIM>(order, 1); 
	}
	
private:
	tiny_vector<unsigned int, DIM> order_;
	tiny_vector<int, DIM> base_;
};

#endif //!< ARRAY_STORAGE_H